
`include "common_header.verilog"

//  *************************************************************************
//  File : mtip_xsync
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized Reproduction or Use is Expressly Prohibited. 
//  Copyright (c) 2001 MoreThanIP.com, Germany
//  Designed by : Francois Balay
//  fbalay@morethanip.com
//  *************************************************************************
//  Description: Generic clock domain crossing synchronization registers.
//  Version    : $Id: mtip_xsync.v,v 1.6 2018/11/13 11:57:05 dk Exp $
//  *************************************************************************

module mtip_xsync (
   data_in,
   reset,
   clk,
   data_s);

parameter NWIDTH      = 8;

input   [NWIDTH - 1:0] data_in;         //  unsynchronized input data
input   reset;                          //  output clock domain
input   clk;                            //  output clock to synchronize to
output  [NWIDTH - 1:0] data_s;          //  synchronized output

`ifndef MTIP_XSYNC_GLOBAL_RANDOM
// ------------------------------------------
// non-randomized, synthesizable synchronizer
// ------------------------------------------

   reg     [NWIDTH - 1:0] data_s; 
   reg     [NWIDTH - 1:0] data_reg1; 

   //  use double register for synchronization
   //  ---------------------------------------

   always @(posedge reset or posedge clk)
      begin : process_1
      if (reset == 1'b 1)
         begin
         data_reg1 <= {(NWIDTH ){1'b 0}};	
         data_s    <= {(NWIDTH ){1'b 0}};	
         end
      else
         begin
         data_reg1 <= data_in;	
         data_s    <= data_reg1;	
         end
      end

`else   
// ---------------------------------------------------------------------
// Metastability modeling. Inserts a  random delays of -1/0 for the bits
// that changed n data_in every time clk toggles from 0 -> 1
// ---------------------------------------------------------------------

   integer x;
   reg  [NWIDTH - 1:0] data_s; 
   reg  [NWIDTH - 1:0] data_reg1; 
   reg  [31:0] rnd;
   reg  [NWIDTH-1:0] data_in_delayed;
   reg  [NWIDTH-1:0] data_in_previous;
   wire [NWIDTH-1:0] data_in_changed;

        reg asrt_doublechange;

   // Capture changes on data_in. When the capture clock changes, only the bits 
   // that have changed last are subject to metastability. This of course requires that the input
   // data is coming from a flop, otherwise as the simulation engine evaluates, the input may change
   // more than once in zero time.
   //
   // when data_in changes or clk changes from 0->1, update data_in_previous with the last value
   // of data_in. this is used to calculate data_in_changed that indicates which were the last bits
   // to change

   // Use two always processes to latch the previous value of data_in. This trick helps when data_in
   // and clk changes in the same simulator time
   always @(data_in)
           data_in_delayed <= data_in;

   // Use data_in and clk to latch data_in_previous so only the first change is metastable
   // If data_in does not change before the next clk, then no more randomness to the output is injected
   always @(data_in or posedge clk or reset)            // added reset to flush history on both edges!
           data_in_previous <= data_in_delayed;

   assign data_in_changed = data_in_previous ^ data_in;

   always @(data_in)
           asrt_doublechange <= |(data_in_previous ^ data_in_delayed);


   // Initialize the random vector
   initial
   begin
      rnd = $random();
   end

   always @(posedge reset or posedge clk)
      begin : synchronizer
      if (reset == 1'b 1)
         begin
         data_reg1 <= {(NWIDTH ){1'b 0}};	
         data_s    <= {(NWIDTH ){1'b 0}};	
         end
      else
         begin

         if (|data_in_changed)
            begin
            for( x=0; x < NWIDTH; x=x+1) 
               begin
               // Every 32 bits recalculate a random value
               if ((x%32) == 0) rnd = $random( rnd );

               // Randomize the bits that have changed on l->h clock transition
               data_reg1[x] <= data_in_changed[x] ? rnd[x%32] : data_in_delayed[x];	
               end
            end
         else
            begin
            data_reg1 <= data_in_delayed;
            end

         data_s <= data_reg1;

         end
      end

`endif

`ifdef MTIP_XSYNC_DISPLAY
initial $display("mtip_xsync:%m.data_reg1[%0d:0]",NWIDTH-1);
`endif

endmodule // module mtip_xsync

